package com.deschen.security.browser;

import com.deschen.security.core.authentication.AbstractChannelSecurityConfig;
import com.deschen.security.core.authentication.mobile.config.MobileAuthenticationSecurityConfig;
import com.deschen.security.core.properties.SecurityConstants;
import com.deschen.security.core.properties.SecurityProperties;
import com.deschen.security.core.validate.code.config.ValidateCodeSecurityConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.social.security.SpringSocialConfigurer;

import javax.sql.DataSource;

/**
 * @Author deschen
 * @Create 2019/8/14
 * @Description 浏览器安全配置
 * @Since 1.0.0
 */
@Configuration
@Slf4j
public class BrowserSecurityConfig extends AbstractChannelSecurityConfig {

    @Autowired
    private SecurityProperties securityProperties;


    @Autowired
    private DataSource dataSource;

    @Autowired
    @Qualifier("customUserDetailService")
    private UserDetailsService userDetailsService;

    @Autowired
    private SpringSocialConfigurer customSpringSocialConfigurer;


    @Autowired
    private MobileAuthenticationSecurityConfig mobileAuthenticationSecurityConfig;

    @Autowired
    private ValidateCodeSecurityConfig validateCodeSecurityConfig;

    /**
     * 加密解密接口
     * 方法：
     * 1. String encode(CharSequence rawPassword)随机生成盐进行加密，-》
     * 优点：同样密码，随机生成不同盐加密后都不一样，避免被人破解密码
     * 2. boolean matches(CharSequence rawPassword, String encodedPassword); -》
     * 浏览器登入密码与存在数据库的密码比较，为true就通过
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 用于把 记住我功能 的token信息存到数据库以及编辑
     * @return
     */
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository =
                new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
//        jdbcTokenRepository.setCreateTableOnStartup(true);   // 自动把数据表配置到数据库，下次要删除掉
        return jdbcTokenRepository;
    }

    /**
     * 用来配置用户签名服务，主要是user-details 机制，你还可以给予用户赋予角色
     * @param auth 签名管理器构造器， 用于构建用户具体权限控制
     *  定义用户（ user ）、密码（ password ）和角色（ role ），在默认的情况下
     *  登入用户：user； 密码：控制器显示一大串字符
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }

    /**
     *  用来配置Filter 链
     *  @param web 主要是配置Filter链的内容，可以配置Filter链忽略哪些内容。
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    /**
     * 用来配置拦截保护的请求，比如什么请求放行，什么请求需要验证
     *
     * @param http 安全请求对象
     *             指定用户和角色与对应 URL 的访问权限
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 登入配置类
        applyPasswordAuthenticationConfig(http);

        http.
//                // 设置验证码过滤器在UsernamePasswordAuthenticationFilter之前
//                addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
                // 配置验证码配置类
                apply(validateCodeSecurityConfig)
                .and()
                .apply(mobileAuthenticationSecurityConfig)      // 添加手机认证配置，相当于直接在这里配置
                .and()
                .apply(customSpringSocialConfigurer)
                // 记住我功能
                .and()
                .rememberMe()
                .tokenRepository(persistentTokenRepository()) // tokenRepository配置，用于存储数据库
                .tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())  // 存储有效时间
                .userDetailsService(userDetailsService)
                .and()
                .authorizeRequests()
                .antMatchers(
                        SecurityConstants.DEFAULT_UNAUTHENTICATION_URL,
                        securityProperties.getBrowser().getLoginPage(),  // 登入页面
                        securityProperties.getBrowser().getSignupUrl(),
                        "/error",    // springboot默认错误链接，避免被"/authentication/require"拦截
                        SecurityConstants.DEFAULT_VALIDATE_CODE_URL_PREFIX + "/*",  // 验证码获取请求
                        "/code/imageValidateCode",  // todo 测试用
                        "/register"
                ).permitAll()   // 登入页面请求无条件允许通过
                .anyRequest()
                .authenticated()
                .and()
                .csrf().disable();  //停用csrf功能

    }




}
